package com.shujia.scala

import scala.io.{BufferedSource, Source}
import scala.collection.mutable.ListBuffer

object Demo09List {
  def main(args: Array[String]): Unit = {
    /**
     * Scala中的数据容器整体分为两类：
     * mutable：可变的
     * immutable：不可变的（默认）
     *
     * List列表：有序，不唯一
     */

    // 创建List：需要指定一个泛型，以及需要传入N个符合泛型的元素进行List构建
    // xs:Int* 可变参数：可以传入N个Int的类型的参数
    val list01: List[Int] = List[Int](1, 2, 3, 3, 3, 4, 6, 5, 0, 9, 8)
    // 简化定义
    val list02: List[Int] = List(1, 2, 3, 3, 3, 4, 6, 5, 0, 9, 8)

    println(5 :: 6 :: 7::list01) // 默认将元素添加到list左边，最终会返回一个新的List

    println(list01)
    println(list02)

    // List的一些基础操作
    println(list01.sum)
    println(list01.max)
    println(list01.min)
    println(list01.size)
    println(list01.length)
    println(list01.head) // 提取第一个元素
    println(list01.tail) // 提取除一个元素之外的所有元素
    println(list01.isEmpty) // 判断为空
    println(list01.nonEmpty) // 判断非空
    println(list01.reverse) // 反转
    println(list01.sorted) // 从小到大排序
    println(list01.take(3)) // 提取前面N个元素
    println(list01.drop(4)) // 剔除前面N个元素
    println(list01.distinct)

    // List中常见的高级操作：map、flatMap、filter、groupBy、foreach、sortBy、sortWith

    /**
     * map：同foreach非常类似，可以对List中的每个元素进行处理，处理完后需要有返回值
     * 接收一个参数f: Int => B
     * Int类型：List中每个元素的类型
     * B类型：表示任意类型，由自己确定，不能是Unit
     *
     * 传入一个元素，返回一个元素
     *
     * 类似SQL中的UDF：传入一行返回一行
     */

    // 将每个元素*2
    def f(i: Int): Int = {
      i * 2
    }


    println(list01.map(f))
    println(list01.map(i => i * 2))
    println(list01.map(_ * 2))

    /**
     * flatMap：同map方法类似，也会对List中的每个元素进行处理，但是最终需要返回一个数据容器（List、Array等），
     * 最终会将返回的数据容器中的每个元素进行展开（扁平化处理）
     * 需要接收一个函数f：String => GenTraversableOnce[B]
     * String类型：List中每个元素的类型
     * GenTraversableOnce类型：指List或者是Array等数据容器公有的特征（类似抽象类）
     * B：用户自定义的类型，不能是Unit
     *
     * 类似SQL中的explode
     *
     * 传入一个元素，返回一批元素（会被扁平化变成多个元素）
     * 类似SQL中的UDTF：传入一行返回多行
     */

    val bs: BufferedSource = Source.fromFile("scala/data/words.txt")

    val wordsList: List[String] = bs
      .getLines()
      .toList

    println(wordsList) // 整体打印
    // 将每个元素遍历打印
    wordsList.foreach(println)

    def mySplit(s: String) = {
      s.split(",").toList
    }

    wordsList.map(mySplit).foreach(println)
    wordsList.flatMap(mySplit).foreach(println)
    wordsList.flatMap((s: String) => {
      s.split(",")
    }).foreach(println)
    wordsList.flatMap(s => s.split(",")).foreach(println)
    wordsList.flatMap(_.split(",")).foreach(println)


    bs.close()

    // List是不可变的，对List做的所有操作，并不会影响List本身，某些情况下得到的List实际上是一个新的List
    println(list01)

    /**
     * filter：用于对List中的每个元素进行过滤
     * 需要接收函数p：Int => Boolean
     * Int：List中每个元素的类型
     * Boolean：用于决定最终对元素过不过滤？
     * true：保留
     * false：过滤
     */

    // 将奇数过滤出来
    list01.filter(i => i % 2 == 1).foreach(println)

    /**
     * groupBy：用于将数据进行分组
     * 分组后可以再接一些操作进行统计
     * 需要接收一个函数f：String => K
     * String：List中的每个元素的类型
     * K：用户自定义的类型，如果想要将不同的元素分到同一组，那么返回的值得相同
     */

    val stuList: List[String] = List("理科班,张三,70", "文科,赵六,70", "理科,李四,80", "文科班,王五,99")

    // 按照班级分组，统计班级平均分：[班级,平均分]
    stuList.groupBy(s => {
        val clazz: String = s.split(",")(0)
        var groupId: String = ""
        groupId = if (clazz.startsWith("理科"))
          "理科"
        else if (clazz.startsWith("文科"))
          "文科"
        else
          "其它"
        groupId
      }).map(t2 => {
        val clazz: String = t2._1
        val scores: List[Int] = t2._2.map(s => s.split(",")(2).toInt)
        (clazz, scores.sum / scores.size.toDouble)
      })
      .foreach(println)

    /**
     * sortBy：同groupBy类似，指定一个排序字段进行排序
     * 需要接收一个函数f：String => B
     * String：List中的每个元素
     * B：用户自己定义的类型
     */

    // 按照分数降序排序
    stuList.sortBy(s => {
      s.split(",")(2).toInt
    }).foreach(println) // 默认升序
    stuList.sortBy(s => {
      -s.split(",")(2).toInt
    }).foreach(println) // 需要降序可以加上负号

    /**
     * sortWith：可以按照指定的规则进行排序
     * 规则类似compareTo
     */
    // 先按照分数降序排列，再按照班级排列
    stuList.sortWith((s1, s2) => {
      val clazz1: String = s1.split(",")(0)
      val clazz2: String = s2.split(",")(0)
      val score1: Int = s1.split(",")(2).toInt
      val score2: Int = s2.split(",")(2).toInt
      if (score1 == score2) {
        // 分数相同再进行班级的排序
        clazz1 < clazz2
      } else {
        // 分数不同则直接按照分数降序排列
        score1 > score2
      }
    }).foreach(println)

    /**
     * foreach：同map非常类似，map是有返回值的，foreach不需要返回值
     */

    // 将每个元素扩大3倍并打印
    println(list01.map(_ * 3)) // 整体打印
    list01.map(_ * 3).foreach(println) // 逐个打印
    list01.foreach(i => println(i * 3)) // 边扩大3倍边打印

    // 可变的List：ListBuffer
    // 除了上诉的操作之外，还可以进行元素的增加、删除、修改操作

    val lb01: ListBuffer[Int] = ListBuffer[Int](1, 2, 3, 3, 0, 4, 6, 5, 7, 8, 9, 10, 0)
    println(lb01)

    // 增加元素
    lb01.append(11) // 默认从尾部添加N个元素
    lb01.append(12,13)
    println(lb01)

    lb01.insert(3,4,4) // 指定位置插入N个元素
    println(lb01)

    lb01.insertAll(4,List(5,5))
    println(lb01)

    lb01.+=(14)
    lb01 += 14
    println(lb01)

    // 删除元素
    lb01 -= 13 // 默认从左往右将指定的元素删除一次
    lb01 -= 3
    println(lb01)

    lb01.remove(4) // 指定下标进行删除
    lb01.remove(4,3) // 指定下标进行删除N个元素
    println(lb01)

    // 修改元素
    lb01(11) = 10
    println(lb01)

    lb01.update(10,0) // 将指定位置的元素进行修改
    println(lb01)

  }

}
